import os
import json
import requests
from typing import Dict, Optional, Any
from pathlib import Path
import boto3
from botocore.exceptions import ClientError


class FileStorageAdapter:
    """文件存储适配器，用于本地文件读写操作"""
    def __init__(self, file_path: str):
        """
        初始化文件存储适配器
        
        参数:
            file_path: 文件路径
        """
        self.file_path = file_path

    def read(self) -> bytes:
        """
        读取文件内容
        
        返回:
            文件内容的字节数据，如果文件不存在则返回空字节
        """
        if os.path.exists(self.file_path):
            with open(self.file_path, 'rb') as f:
                return f.read()
        return b''

    def write(self, data: bytes):
        """
        写入数据到文件
        
        参数:
            data: 要写入的字节数据
        """
        with open(self.file_path, 'wb') as f:
            f.write(data) 



class BaseStorageAdapter:
    """存储适配器基类"""
    def __init__(self, encrypted_data: Dict):
        self.data = encrypted_data
        self.validate_data()

    def validate_data(self):
        """验证加密数据格式"""
        required_fields = {'ciphertext', 'salt', 'nonce', 'tag'}
        if not all(field in self.data for field in required_fields):
            raise ValueError("Invalid encrypted data format")

class S3StorageAdapter(BaseStorageAdapter):
    """S3兼容存储适配器 (AWS S3, MinIO, 阿里云OSS等)"""
    def __init__(self, encrypted_data: Dict):
        super().__init__(encrypted_data)
        self.s3 = None  # 在export/import时创建客户端
    
    def _create_s3_client(self, **kwargs):
        """
        创建S3客户端
        
        支持参数:
        - endpoint_url: S3兼容服务的URL端点
        - region_name: 区域名称
        - aws_access_key_id: 访问密钥ID
        - aws_secret_access_key: 秘密访问密钥
        - verify: 是否验证SSL证书
        """
        # 提取核心参数
        endpoint_url = kwargs.pop('endpoint_url', None)
        verify_ssl = kwargs.pop('verify', True)
        
        # 核心客户端配置
        client_kwargs = {
            'config': boto3.session.Config(
                signature_version='s3v4',
                verify=verify_ssl
            )
        }
        
        # 添加可选参数
        if endpoint_url:
            client_kwargs['endpoint_url'] = endpoint_url
        
        # 添加其他关键参数(region_name, aws_access_key_id等)
        for key in ['region_name', 'aws_access_key_id', 'aws_secret_access_key']:
            if key in kwargs and kwargs[key]:
                client_kwargs[key] = kwargs[key]
        
        # 创建客户端
        return boto3.client('s3', **client_kwargs)

    def export_to_s3(self, bucket_name: str, object_key: str, **kwargs) -> bool:
        """
        导出到S3兼容存储
        
        必要参数:
        :param bucket_name: 存储桶名称
        :param object_key: 存储对象键
        
        可选参数:
        :param endpoint_url: S3兼容服务的URL端点(例如: http://minio.example.com:9000)
        :param region_name: 区域名称
        :param aws_access_key_id: 访问密钥ID
        :param aws_secret_access_key: 秘密访问密钥
        :param verify: 是否验证SSL证书(默认True)
        
        :return: 操作是否成功
        """
        try:
            # 创建S3客户端
            s3_client = self._create_s3_client(**kwargs)
            
            # 准备额外参数
            put_args = {
                'Bucket': bucket_name,
                'Key': object_key,
                'Body': json.dumps(self.data)
            }
            
            # 当使用AWS S3而非其他兼容服务时，添加服务端加密
            if 'endpoint_url' not in kwargs:
                put_args['ServerSideEncryption'] = 'AES256'
                
            # 上传对象
            response = s3_client.put_object(**put_args)
            return response['ResponseMetadata']['HTTPStatusCode'] == 200
        except ClientError as e:
            error_msg = f"S3存储操作失败: {str(e)}"
            if hasattr(e, 'response') and 'Error' in e.response:
                error_msg = f"S3存储操作失败: {e.response['Error'].get('Message', str(e))}"
            raise ConnectionError(error_msg) from e
        except Exception as e:
            raise ConnectionError(f"S3存储操作失败: {str(e)}") from e

    @classmethod
    def import_from_s3(cls, bucket_name: str, object_key: str, **kwargs) -> Dict:
        """
        从S3兼容存储导入配置
        
        必要参数:
        :param bucket_name: 存储桶名称
        :param object_key: 存储对象键
        
        可选参数:
        :param endpoint_url: S3兼容服务的URL端点(例如: http://minio.example.com:9000)
        :param region_name: 区域名称
        :param aws_access_key_id: 访问密钥ID
        :param aws_secret_access_key: 秘密访问密钥
        :param verify: 是否验证SSL证书(默认True)
        
        :return: 解密前的配置数据
        """
        try:
            # 创建临时适配器用于创建S3客户端
            temp_adapter = cls({})
            s3_client = temp_adapter._create_s3_client(**kwargs)
            
            # 获取对象
            response = s3_client.get_object(Bucket=bucket_name, Key=object_key)
            return json.loads(response['Body'].read().decode())
        except ClientError as e:
            error_msg = f"S3存储读取失败: {str(e)}"
            if hasattr(e, 'response') and 'Error' in e.response:
                error_msg = f"S3存储读取失败: {e.response['Error'].get('Message', str(e))}"
            raise ConnectionError(error_msg) from e
        except Exception as e:
            raise ConnectionError(f"S3存储读取失败: {str(e)}") from e

class WebDavStorageAdapter(BaseStorageAdapter):
    """WebDAV服务适配器 (兼容Nextcloud、ownCloud等WebDAV服务)"""
    def __init__(self, encrypted_data: Dict):
        super().__init__(encrypted_data)
        self.session = requests.Session()

    def _create_webdav_client(self, **kwargs):
        """
        创建WebDAV客户端
        
        支持参数:
        :param webdav_url: WebDAV服务地址 (必需)
        :param username: WebDAV用户名 (必需)
        :param password: WebDAV密码 (必需)
        :param verify_ssl: 是否验证SSL证书 (可选，默认True)
        :param timeout: 连接超时时间（秒） (可选，默认30)
        :param proxy: 代理服务器地址 (可选)
        """
        webdav_url = kwargs.get('webdav_url')
        if not webdav_url:
            raise ValueError("缺少WebDAV服务地址(webdav_url)")
            
        username = kwargs.get('username')
        if not username:
            raise ValueError("缺少WebDAV用户名(username)")
            
        password = kwargs.get('password')
        if not password:
            raise ValueError("缺少WebDAV密码(password)")
        
        # 设置请求会话
        session = requests.Session()
        session.auth = (username, password)
        session.verify = kwargs.get('verify_ssl', True)
        
        # 设置超时
        timeout = kwargs.get('timeout', 30)
        
        # 设置代理
        if 'proxy' in kwargs and kwargs['proxy']:
            session.proxies = {'http': kwargs['proxy'], 'https': kwargs['proxy']}
            
        return {
            'session': session,
            'base_url': webdav_url,
            'timeout': timeout
        }

    def export_to_webdav(self, webdav_url: str, username: str, password: str, file_path: str, **kwargs) -> bool:
        """
        导出到WebDAV服务
        
        必要参数:
        :param webdav_url: WebDAV服务地址 (如 https://nextcloud.example.com/remote.php/dav/files/username/)
        :param username: WebDAV用户名
        :param password: WebDAV密码
        :param file_path: 存储文件路径 (如 'configs/secure_config.json')
        
        可选参数:
        :param verify_ssl: 是否验证SSL证书 (默认True)
        :param timeout: 连接超时时间 (默认30秒)
        :param proxy: 代理服务器地址
        
        :return: 操作是否成功
        """
        try:
            # 创建WebDAV客户端
            client = self._create_webdav_client(
                webdav_url=webdav_url,
                username=username,
                password=password,
                **kwargs
            )
            
            # 准备请求参数
            session = client['session']
            url = f"{client['base_url'].rstrip('/')}/{file_path.lstrip('/')}"
            timeout = client['timeout']
            
            # 准备上传内容
            data = json.dumps(self.data)
            
            # 确保目录结构存在 (先创建父目录)
            parent_dir = '/'.join(file_path.split('/')[:-1])
            if parent_dir:
                parent_url = f"{client['base_url'].rstrip('/')}/{parent_dir.lstrip('/')}"
                try:
                    # 尝试创建目录 (MKCOL方法)
                    session.request('MKCOL', parent_url, timeout=timeout)
                except requests.exceptions.RequestException:
                    # 忽略目录已存在错误
                    pass
                    
            # 上传文件 (PUT方法)
            headers = {'Content-Type': 'application/json'}
            response = session.put(
                url=url,
                data=data,
                headers=headers,
                timeout=timeout
            )
            response.raise_for_status()
            return True
        except requests.exceptions.RequestException as e:
            error_msg = f"WebDAV服务连接失败: {str(e)}"
            if hasattr(e, 'response') and e.response is not None:
                status_code = e.response.status_code
                error_msg = f"WebDAV服务错误 (HTTP {status_code}): {str(e)}"
            raise ConnectionError(error_msg) from e
        except Exception as e:
            raise ConnectionError(f"WebDAV导出失败: {str(e)}") from e

    @classmethod
    def import_from_webdav(cls, webdav_url: str, username: str, password: str, file_path: str, **kwargs) -> Dict:
        """
        从WebDAV服务导入配置
        
        必要参数:
        :param webdav_url: WebDAV服务地址 (如 https://nextcloud.example.com/remote.php/dav/files/username/)
        :param username: WebDAV用户名
        :param password: WebDAV密码
        :param file_path: 存储文件路径 (如 'configs/secure_config.json')
        
        可选参数:
        :param verify_ssl: 是否验证SSL证书 (默认True)
        :param timeout: 连接超时时间 (默认30秒)
        :param proxy: 代理服务器地址
        
        :return: 解密前的配置数据
        """
        try:
            # 创建临时适配器来创建WebDAV客户端
            temp_adapter = cls({})
            client = temp_adapter._create_webdav_client(
                webdav_url=webdav_url,
                username=username,
                password=password,
                **kwargs
            )
            
            # 准备请求参数
            session = client['session']
            url = f"{client['base_url'].rstrip('/')}/{file_path.lstrip('/')}"
            timeout = client['timeout']
            
            # 获取文件 (GET方法)
            response = session.get(url, timeout=timeout)
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            error_msg = f"WebDAV服务连接失败: {str(e)}"
            if hasattr(e, 'response') and e.response is not None:
                status_code = e.response.status_code
                if status_code == 404:
                    error_msg = f"WebDAV文件不存在: {file_path}"
                else:
                    error_msg = f"WebDAV服务错误 (HTTP {status_code}): {str(e)}"
            raise ConnectionError(error_msg) from e
        except json.JSONDecodeError as e:
            raise ValueError(f"无效的JSON数据格式: {str(e)}") from e
        except Exception as e:
            raise ConnectionError(f"WebDAV导入失败: {str(e)}") from e

class StorageOperations:
    """统一存储操作门面类"""
    @staticmethod
    def handle_export(adapter_type: str, encrypted_data: Dict, **kwargs) -> bool:
        """
        统一导出操作
        :param adapter_type: 适配器类型 (s3/webdav)
        :param encrypted_data: 已加密的配置数据
        :param kwargs: 存储适配器所需参数
        """
        # 基本适配器参数检查
        adapters = {
            's3': (S3StorageAdapter, ['bucket_name', 'object_key']),
            'webdav': (WebDavStorageAdapter, ['webdav_url', 'username', 'password', 'file_path'])
        }
        
        if adapter_type not in adapters:
            raise ValueError(f"不支持的适配器类型: {adapter_type}")

        adapter_class, required_params = adapters[adapter_type]
        for param in required_params:
            if param not in kwargs:
                raise ValueError(f"缺少必要参数: {param}")

        # 创建适配器实例
        adapter = adapter_class(encrypted_data)
        
        # 执行适配器相应的导出方法
        export_method = getattr(adapter, f'export_to_{adapter_type}')
        
        # 从kwargs中提取export方法所需参数
        # 首先提取必要参数
        export_args = {param: kwargs[param] for param in required_params}
        
        # 对于S3适配器，传递额外的可选参数
        if adapter_type == 's3':
            # 可选的S3参数
            s3_optional_params = [
                'endpoint_url', 'region_name', 'aws_access_key_id', 
                'aws_secret_access_key', 'verify'
            ]
            # 将所有存在的可选参数添加到export_args
            for param in s3_optional_params:
                if param in kwargs:
                    export_args[param] = kwargs[param]
        
        # 执行导出
        return export_method(**export_args)

    @staticmethod
    def handle_import(adapter_type: str, **kwargs) -> Dict:
        """
        统一导入操作
        :param adapter_type: 适配器类型 (s3/webdav)
        :param kwargs: 存储适配器所需参数
        """
        # 基本适配器参数检查
        importers = {
            's3': (S3StorageAdapter.import_from_s3, ['bucket_name', 'object_key']),
            'webdav': (WebDavStorageAdapter.import_from_webdav, ['webdav_url', 'username', 'password', 'file_path'])
        }

        if adapter_type not in importers:
            raise ValueError(f"不支持的适配器类型: {adapter_type}")

        importer_func, required_params = importers[adapter_type]
        for param in required_params:
            if param not in kwargs:
                raise ValueError(f"缺少必要参数: {param}")

        # 准备导入参数
        import_args = {param: kwargs[param] for param in required_params}
        
        # 对于S3适配器，传递额外的可选参数
        if adapter_type == 's3':
            # 可选的S3参数
            s3_optional_params = [
                'endpoint_url', 'region_name', 'aws_access_key_id', 
                'aws_secret_access_key', 'verify'
            ]
            # 将所有存在的可选参数添加到import_args
            for param in s3_optional_params:
                if param in kwargs:
                    import_args[param] = kwargs[param]
        
        # 执行导入
        return importer_func(**import_args)